#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unordered_map>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>

#define COMMAND_SIZE 1024  // 定义命令行输入的最大长度
#define FORMAT "[%s@%s %s]# "  // 定义命令行提示符格式

// 下面是shell定义的全局数据

// 1. 命令行参数表
#define MAXARGC 128  // 最大参数个数
char *g_argv[MAXARGC];  // 存储命令及其参数的数组
int g_argc = 0;  // 参数个数

// 2. 环境变量表
#define MAX_ENVS 100  // 最大环境变量个数
char *g_env[MAX_ENVS];  // 存储环境变量的数组
int g_envs = 0;  // 环境变量个数

// 3. 别名映射表
std::unordered_map<std::string, std::string> alias_list;  // 用于管理命令别名

// 4. 关于从定向，我们关心的内容
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDID 3

int redir = NONE_REDIR;
std::string filename;

// for test
char cwd[1024];  // 当前工作目录
char cwdenv[1024];  // 当前工作目录的环境变量形式

// last exit code
int lastcode = 0;  // 上次命令的退出码

// 获取用户名
const char *GetUserName()
{
    const char *name = getenv("USER");  // 从环境变量中获取用户名
    return name == NULL ? "None" : name;  // 如果获取失败，返回"None"
}

// 获取主机名
const char *GetHostName()
{
    const char *hostname = getenv("HOSTNAME");  // 从环境变量中获取主机名
    return hostname == NULL ? "None" : hostname;  // 如果获取失败，返回"None"
}

// 获取当前工作目录
const char *GetPwd()
{
    const char *pwd = getcwd(cwd, sizeof(cwd));  // 使用getcwd获取当前工作目录
    if(pwd != NULL)
    {
        snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);  // 将当前目录更新到环境变量中
        putenv(cwdenv);
    }
    return pwd == NULL ? "None" : pwd;  // 如果获取失败，返回"None"
}

// 获取用户主目录
const char *GetHome()
{
    const char *home = getenv("HOME");  // 从环境变量中获取用户主目录
    return home == NULL ? "" : home;  // 如果获取失败，返回空字符串
}

// 初始化环境变量
void InitEnv()
{
    extern char **environ;  // 外部环境变量指针
    memset(g_env, 0, sizeof(g_env));  // 初始化环境变量数组
    g_envs = 0;

    // 从系统环境变量中复制
    for(int i = 0; environ[i]; i++)
    {
        g_env[i] = (char*)malloc(strlen(environ[i])+1);  // 为每个环境变量分配内存
        strcpy(g_env[i], environ[i]);  // 复制环境变量
        g_envs++;
    }
    g_env[g_envs++] = (char*)"HAHA=for_test";  // 添加一个测试用环境变量
    g_env[g_envs] = NULL;

    // 将自定义环境变量导出到系统环境
    for(int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}

// 实现cd命令
bool Cd()
{
    if(g_argc == 1)  // 如果没有参数，默认切换到用户主目录
    {
        std::string home = GetHome();
        if(home.empty()) return true;
        chdir(home.c_str());
    }
    else
    {
        std::string where = g_argv[1];  // 获取目标目录
        if(where == "-")  // 如果参数为"-", TODO: 处理cd -
        {
            // Todu
        }
        else if(where == "~")  // 如果参数为"~", TODO: 处理cd ~
        {
            // Todu
        }
        else
        {
            chdir(where.c_str());  // 切换到指定目录
        }
    }
    return true;
}

// 实现echo命令
void Echo()
{
    if(g_argc == 2)  // 如果只有一个参数
    {
        std::string opt = g_argv[1];
        if(opt == "$?")  // 如果参数为"$?", 输出上次命令的退出码
        {
            std::cout << lastcode << std::endl;
            lastcode = 0;
        }
        else if(opt[0] == '$')  // 如果参数以"$"开头，尝试输出对应的环境变量值
        {
            std::string env_name = opt.substr(1);
            const char *env_value = getenv(env_name.c_str());
            if(env_value)
                std::cout << env_value << std::endl;
        }
        else  // 否则直接输出参数内容
        {
            std::cout << opt << std::endl;
        }
    }
}

// 从完整路径中提取当前目录名
std::string DirName(const char *pwd)
{
#define SLASH "/"
    std::string dir = pwd;
    if(dir == SLASH) return SLASH;
    auto pos = dir.rfind(SLASH);  // 找到最后一个"/"的位置
    if(pos == std::string::npos) return "BUG?";
    return dir.substr(pos+1);  // 提取最后一个"/"之后的部分
}

// 生成命令行提示符
void MakeCommandLine(char cmd_prompt[], int size)
{
    snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
}

// 输出命令行提示符
void PrintCommandPrompt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt, sizeof(prompt));
    printf("%s", prompt);
    fflush(stdout);  // 刷新输出缓冲区
}

// 获取用户输入的命令
bool GetCommandLine(char *out, int size)
{
    char *c = fgets(out, size, stdin);  // 从标准输入读取一行
    if(c == NULL) return false;
    out[strlen(out)-1] = 0;  // 去掉末尾的换行符
    if(strlen(out) == 0) return false;
    return true;
}

// 命令行解析，将命令行分割成命令和参数
bool CommandParse(char *commandline)
{
#define SEP " "
    g_argc = 0;
    g_argv[g_argc++] = strtok(commandline, SEP);  // 使用strtok分割命令行
    while((bool)(g_argv[g_argc++] = strtok(nullptr, SEP)));
    g_argc--;
    return g_argc > 0 ? true:false;
}

// 打印命令行参数（调试用）
void PrintArgv()
{
    for(int i = 0; g_argv[i]; i++)
    {
        printf("argv[%d]->%s\n", i, g_argv[i]);
    }
    printf("argc: %d\n", g_argc);
}

// 检测并执行内置命令
bool CheckAndExecBuiltin()
{
    std::string cmd = g_argv[0];
    if(cmd == "cd")
    {
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
        Echo();
        return true;
    }
    else if(cmd == "export")
    {
    }
    else if(cmd == "alias")
    {
       // std::string nickname = g_argv[1];
       // alias_list.insert(k, v);
    }

    return false;
}

// 执行外部命令
int Execute()
{
    pid_t id = fork();  // 创建子进程
    if(id == 0)
    {   //子进程检查从定向情况
        if(redir==INPUT_REDIR)
        {
          
          int fd = open(filename.c_str(),O_RDONLY|O_CREAT,0666);
          if(fd<0) exit(1);
          dup2(fd,0);
          close(fd);
        }
        else if(redir==OUTPUT_REDIR)
        {

          int fd = open(filename.c_str(),O_WRONLY|O_CREAT|O_TRUNC,0666);
          if(fd<0) exit(2);
          dup2(fd,1);
          close(fd);
        }
        else if(redir==APPEND_REDID)
        {
          int fd = open(filename.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
          if(fd<0)exit(2);
          dup2(fd,1); 
          close(fd);
        }
        else
        {}
        //child
        execvp(g_argv[0], g_argv);  // 在子进程中执行命令
        exit(1);
    }

    int status = 0;
    pid_t rid = waitpid(id, &status, 0);  // 父进程等待子进程结束
    if(rid > 0)
    {
        lastcode = WEXITSTATUS(status);  // 获取子进程的退出码
    }
    return 0;
}

// 释放分配的内存
void destory()
{
    for(int i = 0; i < g_envs; i++)
    {
        if(g_env[i] != NULL)
        {
            free(g_env[i]);  // 释放环境变量表中分配的内存
            g_env[i] = NULL;
        }
    }
    g_envs = 0;
}

void TrimSpace(char* cmd,int end)
{
  while(isspace(cmd[end]))
  {
    end++;
  }
}

void RedirCheck(char cmd[])
{
    redir = NONE_REDIR;//每一次刚开始都是默认没有重定向                                                                           
    filename.clear();
    int start = 0;
    int end = strlen(cmd)-1;//下标为最后一个字符的位置
    while(end>start)
    {
      if(cmd[end]=='<')
      {
        cmd[end++]=0;
        TrimSpace(cmd,end);
        redir = INPUT_REDIR;
        filename = cmd + end;
        break;
      }

      else if(cmd[end]=='>')
      {
        if(cmd[end-1]=='>')
        {
          //>>
          cmd[end-1]=0;
          redir = APPEND_REDID;
        }
        else
        {
          //> 
          redir = OUTPUT_REDIR;
        }
          cmd[end++]=0; 
          TrimSpace(cmd,end);
          filename = cmd + end;
          break;
      }
      else
      {
        end--;  
      }

    }
}


// 主函数
int main()
{
    InitEnv();  // 初始化环境变量

    while(true)
    {   
        
        PrintCommandPrompt();  // 输出命令行提示符

        char commandline[COMMAND_SIZE];
        if(!GetCommandLine(commandline, sizeof(commandline)))  // 获取用户输入的命令
            continue;
        
        //命令行分析
        RedirCheck(commandline);
    //    printf("redir: %d ,filename: %s\n",redir,filename.c_str());
        if(!CommandParse(commandline))  // 解析命令行
            continue;
        //PrintArgv();

        if(CheckAndExecBuiltin())  // 检测并执行内置命令
            continue;

        Execute();  // 执行外部命令
    }

    destory(); // 在程序退出前释放内存
    return 0;
}
